package de.hsb.ai.agent.analyzer;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.jgrapht.GraphPath;
import org.jgrapht.alg.DijkstraShortestPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.hsb.ai.util.map.ScotlandJadeMap;
import de.hsb.ai.util.map.ScotlandJadeMapHelper.ScotlandJadeEdge;
import de.hsb.ai.util.map.ScotlandJadeMapHelper.ScotlandJadeVertex;
import de.hsb.ai.util.map.ScotlandJadeSubMap;

/**
 * PathFinder is a {@link ScotlandJadeMap}-based utility class
 * that provides path-finding logic.
 */
public class PathFinder {
	
	public static final Logger logger = LoggerFactory.getLogger("sy.pathfinder");
	
	private ScotlandJadeSubMap map;
	
	private ScotlandJadeVertex startVertex;
	
	private Set<ScotlandJadeVertex> temporarilyUnavailable = Collections.emptySet();
	
	public PathFinder(ScotlandJadeMap map) {
		this.map = new ScotlandJadeSubMap(map);
	}
	
	public void setTemporailyUnavailable(Set<ScotlandJadeVertex> vertices) {
		temporarilyUnavailable = vertices == null ? Collections.<ScotlandJadeVertex>emptySet() : vertices;
	}
	
	/**
	 * Removes all edges we are no longer able to follow as a lack of tickets.
	 * @param startVertex
	 * @param transport
	 * @param quota
	 */
	public void update(ScotlandJadeVertex startVertex, int transport, int quota) {
		this.startVertex = startVertex;
		if (quota == 0) {
			List<ScotlandJadeEdge> edgesToBeRemoved = new LinkedList<ScotlandJadeEdge>();
			for (ScotlandJadeEdge edge : this.map.edgeSet()) {
				if (edge.getType() == transport) {
					edgesToBeRemoved.add(edge);
				}
			}
			logger.warn("NO MORE TICKETS FOR " + transport);
			this.map.removeAllEdges(edgesToBeRemoved);
		}
	}

	/**
	 * Builds the shortest path towards the target and returns the
	 * next move from a list of possible options. 
	 * @param targetVertex
	 * @param options
	 * @return the index or -1 if no path could be found
	 */
	public int getNextMoveTowards(ScotlandJadeVertex targetVertex, List<ScotlandJadeEdge> options) {
		DijkstraShortestPath<ScotlandJadeVertex, ScotlandJadeEdge> algo = new DijkstraShortestPath<ScotlandJadeVertex, ScotlandJadeEdge>(map, startVertex, targetVertex);
		GraphPath<ScotlandJadeVertex, ScotlandJadeEdge> path = algo.getPath();
		
		logger.debug("found path from " + startVertex + " to " + targetVertex + ": " + path);
		
		return path == null ? -1 : options.indexOf(path.getEdgeList().get(0));
	}
	
	public ScotlandJadePath getPathToBestConnectedVertex(ScotlandJadeVertex start) {
		ScotlandJadePath maxEndWeight = null;
		ScotlandJadePath minPathWeight = null;
		
		//Transportmöglichkeiten
		Collection<ScotlandJadeEdge> firstEdges = map.edgesOf(start);
		
		logger.debug("edges for " + start.getIndex() + ": " + firstEdges);
		
		for(ScotlandJadeEdge fe : firstEdges){
			
			//Mögliche Ziele
			ScotlandJadeVertex secondVertex = map.getBase().get(fe.getTargetIndex(start.getIndex()));
			
			if (temporarilyUnavailable.contains(secondVertex) || secondVertex.getType() != ScotlandJadeVertex.Type.EMPTY) continue;
			
//			logger.debug("checking " + secondVertex.getIndex());
			
			//Edges der jeweiligen Ziele
			Collection<ScotlandJadeEdge> secondEdges = map.edgesOf(secondVertex);
			
			for(ScotlandJadeEdge se : secondEdges){
				
				ScotlandJadeVertex endVertex = map.getBase().get(se.getTargetIndex(secondVertex.getIndex()));
				if (temporarilyUnavailable.contains(secondVertex)) continue;
				
//				logger.debug("  checking " + endVertex.getIndex());
				
				int vertexWeight = map.edgesOf(endVertex).size(); 
				for(ScotlandJadeEdge edge: map.edgesOf(endVertex)){
					vertexWeight += edge.getType();
				}
				
				ScotlandJadePath path = new ScotlandJadePath(map, start, endVertex, Arrays.<ScotlandJadeEdge>asList(fe, se), fe.getType() + se.getType(), vertexWeight);
				
				if(minPathWeight == null || path.getWeight() < minPathWeight.getWeight()){
					minPathWeight = path;
				}
				
				if(maxEndWeight == null || path.getEndWeight() > maxEndWeight.getEndWeight()){
					maxEndWeight = path;
				}
				
			}
			
		}
		
		return getBestPerformingPath(minPathWeight, maxEndWeight);
	}

	private static ScotlandJadePath getBestPerformingPath(ScotlandJadePath minPathWeightPath, ScotlandJadePath maxEndWeightPath) {
		//TODO: Noch mal drüberschauen
		if(maxEndWeightPath.getPricePerformance() == minPathWeightPath.getPricePerformance()){
			return minPathWeightPath;
		}
		else if(maxEndWeightPath.getPricePerformance() < minPathWeightPath.getPricePerformance()){
			return maxEndWeightPath;
		}
		return minPathWeightPath;
	}

}
